package com.peachcastle.socket.protocol
{
	import flash.utils.ByteArray;
	import flash.utils.Endian;
	
	import muduo.Datas;
	import muduo.Datas.Data;
	
	import com.peachcastle.tools.Adler32;
	
	import com.peachcastle.debug.Log;
	
	public class Muduo
	{
		private static const PREV_LEN:int = 8;
		
		private var _datas:Datas = null;
		private var _data:muduo.Datas.Data = null;
		
		private var _cmdType:ByteArray = null;
		
		public function Muduo()
		{
			_cmdType = new ByteArray();
			_cmdType.writeUTFBytes("muduo.Datas\0");
		}
		
		public function hsend(params:Object) : ByteArray
		{
			var rtn:ByteArray = new ByteArray();
			var check:ByteArray = new ByteArray();
			var p:ByteArray = this._pack(params);
			
			check.writeInt(_cmdType.length);
			check.writeBytes(_cmdType);
			check.writeBytes(p);
			var checkSum:Number = Adler32.checkSum(check);

			rtn.endian = Endian.BIG_ENDIAN;
			
			rtn.writeInt(Muduo.PREV_LEN + _cmdType.length + p.length);
			rtn.writeInt(_cmdType.length);
			rtn.writeBytes(_cmdType);
			rtn.writeBytes(p);
			rtn.writeInt(checkSum);
			
			return rtn;
		}
		
		public function hrecv(input : ByteArray) : Object
		{
			var rtn : Object = new Object();
			
			if (input.bytesAvailable < 12)
			{
				return null;
			}
			
			rtn.len = input.readInt();
			if (input.bytesAvailable < rtn.len)
			{
				return null;
			}
			
			rtn.typeNameLen = input.readInt();
			rtn.typeName = new ByteArray();
			input.readBytes(rtn.typeName, 0, rtn.typeNameLen);
			
			var p : ByteArray = new ByteArray();
			input.readBytes(p, 0, rtn.len - Muduo.PREV_LEN - rtn.typeNameLen);
			rtn.params = this._unpack(p);
			rtn.adler32 = input.readInt();
			
			return rtn;
		}
		
		private function _unpack(data : ByteArray) : Object
		{
			var r : Object = new Object();
			
			data.endian = Endian.BIG_ENDIAN;
			
			data.position = 0;
			
			var result:Datas = new Datas();
			result.readExternal(data);
			
			var d_len:int = result.d.length;
			var ds_len:int = result.ds.length;
			var i, j = 0;
			for (i = 0; i < d_len; i++)
			{
				r[Data(result.d[i]).k] = Data(result.d[i]).v;
			}
			
			var tableName:String = ''
			d_len = 0;
			for (i=0; i < ds_len; i++)
			{
				tableName = String(i);
				if (Datas(result.ds[i]).hasTableName) tableName = Datas(result.ds[i]).tableName;
				
				d_len = Datas(result.ds[i]).d.length;
				for (j = 0; j < d_len; j++)
				{
					if (!r.hasOwnProperty(tableName)) r[tableName] = { };
					r[tableName][Data(Datas(result.ds[i]).d[j]).k] = Data(Datas(result.ds[i]).d[j]).v;
				}
			}
			
			return r;
		}
		
		private function _pack(data : Object) : ByteArray
		{
			var r : ByteArray = new ByteArray();
			r.endian = Endian.BIG_ENDIAN;
			this._ins_stream(data, r);
			
			return r;
		}
		
		private function _ins_stream(o:Object, s:ByteArray) : void
		{
			_datas = new Datas();
			for (var key : String in o)
			{
				_data = new Data();
				_data.k = key;
				_data.v = String(o[key]);
				
				_datas.d.push(_data);
			}

			_datas.writeExternal(s);
		}
	}
}